﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ClearingRestartingAutomaton.Common;

namespace ClearingRestartingAutomaton.Base
{
    /// <summary>
    /// Assumptions delegate returns the collection of instructions based on the given 
    /// set of positive samples, having the specified maximal width and the given length of contexts.
    /// </summary>
    /// <param name="positiveSamples">Enumeration of positive samples.</param>
    /// <param name="maximalWidth">Maximal width of instructions.</param>
    /// <param name="contextLength">Length of contexts.</param>
    /// <param name="log">Log delegate.</param>
    /// <returns>
    /// The instance of the InstructionCollection class.
    /// </returns>
    public delegate InstructionCollection Assumptions(IEnumerable<string> positiveSamples, int maximalWidth, int contextLength, Action<string> log);

    /// <summary>
    /// The AssumptionsMethods static class contains some default methods
    /// implementing the Assumptions delegate for clearing restarting automata.
    /// </summary>
    public static class AssumptionsMethods
    {
        /// <summary>
        /// Returns the collection of instructions based on the given set of positive samples, 
        /// having the specified maximal width and the given length of contexts. 
        /// Whenever two patterns: xzy and xy occur (as subwords) in the set of positive 
        /// samples, the instruction (x, z, y) is added to the resulting collection of instructions,
        /// provided that it is a correct instruction of a k-clearing restarting automaton
        /// having the specified maximal width.
        /// </summary>
        /// <param name="positiveSamples">Enumeration of positive samples.</param>
        /// <param name="maximalWidth">Maximal width of instructions.</param>
        /// <param name="contextLength">Length of contexts.</param>
        /// <param name="log">Log delegate.</param>
        /// <returns>
        /// The instance of the InstructionCollection class.
        /// </returns>
        public static InstructionCollection ClearingAssumptionsWeak(IEnumerable<string> positiveSamples, int maximalWidth, int contextLength, Action<string> log)
        {
            SortedSet<string> delimitedSubwordSet = new SortedSet<string>(new StringLengthLexComparer());
            SortedSet<Instruction> instructionSet = new SortedSet<Instruction>();
            foreach (string sample in positiveSamples)
            {
                AddDelimitedSubwords(delimitedSubwordSet, sample, maximalWidth);
            }
            AddDelimitedSubwords(delimitedSubwordSet, String.Empty, maximalWidth);
            if (log != null)
            {
                log("FOUND " + delimitedSubwordSet.Count.ToString() + " DELIMITED SUBWORDS OF POSITIVE SAMPLES: " + 
                    String.Join(", ", delimitedSubwordSet) + Environment.NewLine);
            }
            foreach (string sample in positiveSamples)
            {
                AddWeakClearingInstructions(instructionSet, sample, maximalWidth, contextLength, delimitedSubwordSet);
            }
            return new InstructionCollection(instructionSet);
        }

        /// <summary>
        /// Returns the collection of instructions based on the given set of positive samples, 
        /// having the specified maximal width and the given length of contexts. 
        /// Whenever two patterns: xzy and xty occur (as subwords) in the set of positive 
        /// samples, where t is a proper subword of z, the instruction (x, z -> t, y) is added 
        /// to the resulting collection of instructions, provided that it is a correct instruction 
        /// of a k-subword clearing restarting automaton having the specified maximal width.
        /// </summary>
        /// <param name="positiveSamples">Enumeration of positive samples.</param>
        /// <param name="maximalWidth">Maximal width of instructions.</param>
        /// <param name="contextLength">Length of contexts.</param>
        /// <param name="log">Log delegate.</param>
        /// <returns>
        /// The instance of the InstructionCollection class.
        /// </returns>
        public static InstructionCollection SubwordAssumptionsWeak(IEnumerable<string> positiveSamples, int maximalWidth, int contextLength, Action<string> log)
        {
            SortedSet<string> delimitedSubwordSet = new SortedSet<string>(new StringLengthLexComparer());
            SortedSet<Instruction> instructionSet = new SortedSet<Instruction>();
            foreach (string sample in positiveSamples)
            {
                AddDelimitedSubwords(delimitedSubwordSet, sample, maximalWidth);
            }
            AddDelimitedSubwords(delimitedSubwordSet, String.Empty, maximalWidth);
            if (log != null)
            {
                log("FOUND " + delimitedSubwordSet.Count.ToString() + " DELIMITED SUBWORDS OF POSITIVE SAMPLES: " +
                    String.Join(", ", delimitedSubwordSet) + Environment.NewLine);
            }
            foreach (string sample in positiveSamples)
            {
                AddWeakSubwordInstructions(instructionSet, sample, maximalWidth, contextLength, delimitedSubwordSet);
            }
            return new InstructionCollection(instructionSet);
        }

        /// <summary>
        /// Returns the collection of instructions based on the given set of positive samples, 
        /// having the specified maximal width and the given length of contexts. 
        /// The instruction (x, z, y) is included in the resulting collection of instructions
        /// if and only if there are two positive samples w_1 and w_2, such that w_1 can be reduced 
        /// to w_2 by using this instruction, and it is a correct instruction of a k-clearing restarting 
        /// automaton having the specified maximal width.
        /// </summary>
        /// <param name="positiveSamples">Enumeration of positive samples.</param>
        /// <param name="maximalWidth">Maximal width of instructions.</param>
        /// <param name="contextLength">Length of contexts.</param>
        /// <param name="log">Log delegate.</param>
        /// <returns>
        /// The instance of the InstructionCollection class.
        /// </returns>
        public static InstructionCollection ClearingAssumptionsStrong(IEnumerable<string> positiveSamples, int maximalWidth, int contextLength, Action<string> log)
        {
            SortedSet<string> sampleSet = new SortedSet<string>(new StringLengthLexComparer());
            SortedSet<Instruction> instructionSet = new SortedSet<Instruction>();
            foreach (string sample in positiveSamples)
            {
                sampleSet.Add(sample);
            }
            sampleSet.Add(String.Empty);
            foreach (string sample in positiveSamples)
            {
                AddStrongClearingInstructions(instructionSet, sample, maximalWidth, contextLength, sampleSet);
            }
            return new InstructionCollection(instructionSet);
        }

        /// <summary>
        /// Returns the collection of instructions based on the given set of positive samples, 
        /// having the specified maximal width and the given length of contexts. 
        /// The instruction (x, z -> t, y) is included in the resulting collection of instructions
        /// if and only if t is a proper subword of z and there are two positive samples w_1 and w_2, 
        /// such that w_1 can be reduced to w_2 by using this instruction, and it is a correct instruction 
        /// of a k-subword clearing restarting automaton having the specified maximal width.
        /// </summary>
        /// <param name="positiveSamples">Enumeration of positive samples.</param>
        /// <param name="maximalWidth">Maximal width of instructions.</param>
        /// <param name="contextLength">Length of contexts.</param>
        /// <param name="log">Log delegate.</param>
        /// <returns>
        /// The instance of the InstructionCollection class.
        /// </returns>
        public static InstructionCollection SubwordAssumptionsStrong(IEnumerable<string> positiveSamples, int maximalWidth, int contextLength, Action<string> log)
        {
            SortedSet<string> sampleSet = new SortedSet<string>(new StringLengthLexComparer());
            SortedSet<Instruction> instructionSet = new SortedSet<Instruction>();
            foreach (string sample in positiveSamples)
            {
                sampleSet.Add(sample);
            }
            sampleSet.Add(String.Empty);
            foreach (string sample in positiveSamples)
            {
                AddStrongSubwordInstructions(instructionSet, sample, maximalWidth, contextLength, sampleSet);
            }
            return new InstructionCollection(instructionSet);
        }

        private static void AddDelimitedSubwords(ISet<string> delimitedSubwordSet, string sample, int maximalWidth)
        {
            string delimitedSample = StringDefaultValues.LeftDelimiter + sample + StringDefaultValues.RightDelimiter;
            for (int subwordLength = 2; subwordLength <= maximalWidth; ++subwordLength)
            {
                for (int subwordStart = 0; subwordStart <= delimitedSample.Length - subwordLength; ++subwordStart)
                {
                    string delimitedSubword = delimitedSample.Substring(subwordStart, subwordLength);
                    delimitedSubwordSet.Add(delimitedSubword);
                }
            }
        }

        private static void AddWeakClearingInstructions(ISet<Instruction> instructionSet, string sample, int maximalWidth, int contextLength, ISet<string> delimitedSubwordSet)
        {
            string delimitedSample = StringDefaultValues.LeftDelimiter + sample + StringDefaultValues.RightDelimiter;
            for (int ruleLength = 1; ruleLength <= maximalWidth - 2; ++ruleLength)
            {
                for (int ruleStart = 0; ruleStart <= sample.Length - ruleLength; ++ruleStart)
                {
                    int leftContextStart = Math.Max(-1, ruleStart - contextLength);
                    int leftContextLength = ruleStart - leftContextStart;
                    int rightContextStart = ruleStart + ruleLength;
                    int rightContextLength = Math.Min(sample.Length - rightContextStart + 1, contextLength);
                    if (leftContextLength + ruleLength + rightContextLength <= maximalWidth)
                    {
                        string leftContext = delimitedSample.Substring(leftContextStart + 1, leftContextLength);
                        string rule = sample.Substring(ruleStart, ruleLength);
                        string rightContext = delimitedSample.Substring(rightContextStart + 1, rightContextLength);
                        if (delimitedSubwordSet.Contains(leftContext + rightContext))
                        {
                            Instruction instruction = new Instruction(leftContext, rule, String.Empty, rightContext);
                            instructionSet.Add(instruction);
                        }
                    }
                }
            }
        }

        private static void AddWeakSubwordInstructions(ISet<Instruction> instructionSet, string sample, int maximalWidth, int contextLength, ISet<string> delimitedSubwordSet)
        {
            string delimitedSample = StringDefaultValues.LeftDelimiter + sample + StringDefaultValues.RightDelimiter;
            for (int ruleLength = 1; ruleLength <= maximalWidth - 2; ++ruleLength)
            {
                for (int ruleStart = 0; ruleStart <= sample.Length - ruleLength; ++ruleStart)
                {
                    int leftContextStart = Math.Max(-1, ruleStart - contextLength);
                    int leftContextLength = ruleStart - leftContextStart;
                    int rightContextStart = ruleStart + ruleLength;
                    int rightContextLength = Math.Min(sample.Length - rightContextStart + 1, contextLength);
                    if (leftContextLength + ruleLength + rightContextLength <= maximalWidth)
                    {
                        string leftContext = delimitedSample.Substring(leftContextStart + 1, leftContextLength);
                        string rule = sample.Substring(ruleStart, ruleLength);
                        string rightContext = delimitedSample.Substring(rightContextStart + 1, rightContextLength);
                        if (delimitedSubwordSet.Contains(leftContext + rightContext))
                        {
                            Instruction instruction = new Instruction(leftContext, rule, String.Empty, rightContext);
                            instructionSet.Add(instruction);
                        }
                        for (int subwordStart = 0; subwordStart < rule.Length; ++subwordStart)
                        {
                            for (int subwordLength = 1; subwordLength <= rule.Length - subwordStart; ++subwordLength)
                            {
                                if (subwordLength < rule.Length &&
                                    leftContextLength + ruleLength + subwordLength + rightContextLength <= maximalWidth)
                                {
                                    string subword = rule.Substring(subwordStart, subwordLength);
                                    if (delimitedSubwordSet.Contains(leftContext + subword + rightContext))
                                    {
                                        Instruction instruction = new Instruction(leftContext, rule, subword, rightContext);
                                        instructionSet.Add(instruction);
                                    }
                                }
                                else
                                {
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }

        private static void AddStrongClearingInstructions(ISet<Instruction> instructionSet, string sample, int maximalWidth, int contextLength, ISet<string> sampleSet)
        {
            string delimitedSample = StringDefaultValues.LeftDelimiter + sample + StringDefaultValues.RightDelimiter;
            for (int ruleLength = 1; ruleLength <= maximalWidth - 2; ++ruleLength)
            {
                for (int ruleStart = 0; ruleStart <= sample.Length - ruleLength; ++ruleStart)
                {
                    int leftContextStart = Math.Max(-1, ruleStart - contextLength);
                    int leftContextLength = ruleStart - leftContextStart;
                    int rightContextStart = ruleStart + ruleLength;
                    int rightContextLength = Math.Min(sample.Length - rightContextStart + 1, contextLength);
                    if (leftContextLength + ruleLength + rightContextLength <= maximalWidth)
                    {
                        string leftPart = sample.Substring(0, ruleStart);
                        string rightPart = sample.Substring(rightContextStart);
                        if (sampleSet.Contains(leftPart + rightPart))
                        {
                            string leftContext = delimitedSample.Substring(leftContextStart + 1, leftContextLength);
                            string rule = sample.Substring(ruleStart, ruleLength);
                            string rightContext = delimitedSample.Substring(rightContextStart + 1, rightContextLength);
                            Instruction instruction = new Instruction(leftContext, rule, String.Empty, rightContext);
                            instructionSet.Add(instruction);
                        }
                    }
                }
            }
        }

        private static void AddStrongSubwordInstructions(ISet<Instruction> instructionSet, string sample, int maximalWidth, int contextLength, ISet<string> sampleSet)
        {
            string delimitedSample = StringDefaultValues.LeftDelimiter + sample + StringDefaultValues.RightDelimiter;
            for (int ruleLength = 1; ruleLength <= maximalWidth - 2; ++ruleLength)
            {
                for (int ruleStart = 0; ruleStart <= sample.Length - ruleLength; ++ruleStart)
                {
                    int leftContextStart = Math.Max(-1, ruleStart - contextLength);
                    int leftContextLength = ruleStart - leftContextStart;
                    int rightContextStart = ruleStart + ruleLength;
                    int rightContextLength = Math.Min(sample.Length - rightContextStart + 1, contextLength);
                    if (leftContextLength + ruleLength + rightContextLength <= maximalWidth)
                    {
                        string leftPart = sample.Substring(0, ruleStart);
                        string rule = sample.Substring(ruleStart, ruleLength);
                        string rightPart = sample.Substring(rightContextStart);
                        string leftContext = delimitedSample.Substring(leftContextStart + 1, leftContextLength);
                        string rightContext = delimitedSample.Substring(rightContextStart + 1, rightContextLength);
                        if (sampleSet.Contains(leftPart + rightPart))
                        {
                            Instruction instruction = new Instruction(leftContext, rule, String.Empty, rightContext);
                            instructionSet.Add(instruction);
                        }
                        for (int subwordStart = 0; subwordStart < rule.Length; ++subwordStart)
                        {
                            for (int subwordLength = 1; subwordLength <= rule.Length - subwordStart; ++subwordLength)
                            {
                                if (subwordLength < rule.Length &&
                                    leftContextLength + ruleLength + subwordLength + rightContextLength <= maximalWidth)
                                {
                                    string subword = rule.Substring(subwordStart, subwordLength);
                                    if (sampleSet.Contains(leftPart + subword + rightPart))
                                    {
                                        Instruction instruction = new Instruction(leftContext, rule, subword, rightContext);
                                        instructionSet.Add(instruction);
                                    }
                                }
                                else
                                {
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }

    }
}
